#include <buola/gui.h>

#include <buola/algorithm/container.h>
#include <buola/image/cimage.h>
#include <buola/gui/cpropertyfield.h>

#include <buola/widgets/clistwidget.h>

namespace buola { namespace gui {

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
////////////////////////// SSorter //////////////////////////
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

bool CListWidget::SSorter::operator()(SItem *p1,SItem *p2)
{
    if(mReverse)
        return p1->mText>p2->mText;
    else
        return p1->mText<p2->mText;
}

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
/////////////////////////// SItem ///////////////////////////
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

CListWidget::SItem::SItem(CListWidget *pList)
{
    mList=pList;
    mSelected=false;
    mIcon=nullptr;
    mRendererData=nullptr;

    for(int i=0;i<mList->GetFieldCount();i++)
        AddField(mList->mLWF[i]->mField);
}

CListWidget::SItem::~SItem()
{
}

void CListWidget::SItem::AddField(CPropertyField *pField)
{
    pField->mType->InitValue(construct_back(mValues));
}

UAny &CListWidget::SItem::Value(int i)
{
    return *mValues[mList->GetFieldNum(i)];
}

std::wstring CListWidget::SItem::ValueAsText(int)
{
    ///\todo
    return L"";//utf32(mValues[mList->GetFieldNum(i)]->ToString());
}

void CListWidget::SItem::SetText(const std::wstring &pText)
{
    mText=pText;
    mList->OnItemChanged(this);
}

void CListWidget::SItem::SetIcon(PIcon pIcon)
{
    mIcon=pIcon;
    mList->OnItemChanged(this);
}

void CListWidget::SItem::SetImage(const img::CPicture &pImage)
{
    mImage=pImage;
    mList->OnItemChanged(this);
}

void CListWidget::SItem::SetGroup(const std::wstring &pGroup)
{
    mGroup=pGroup;
}

CListWidget::SItem &CListWidget::SItem::operator=(const SItem &pOther)
{
    mText=pOther.mText;
    mIcon=pOther.mIcon;
    mImage=pOther.mImage;
    mValues.clear();
    for(int i=0;i<pOther.mValues.size();i++)
    {
        mValues.emplace_back(new UAny(*pOther.mValues[i]));
    }

    mList->OnItemChanged(this);

    return (*this);
}

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
//////////////////////// CListWidget ////////////////////////
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

int CListWidget::GetFieldNum(int pIndex)
{
    int lCount=mLWF.size();
    for(int i=0;i<lCount;i++)
        if(mLWF[i]->mIndex==pIndex)
            return i;
    return -1;
}

int CListWidget::GetItemNum(TID pID)
{
    int lCount=mLWI.size();
    for(int i=0;i<lCount;i++)
        if(mLWI[i]->mID==pID)
            return i;
    return -1;
}

void CListWidget::LWCopyList(CListWidget *pOther)
{
    for(int i=0;i<pOther->mLWI.size();i++)
    {
        mLWI.push_back(pOther->mLWI[i]);
    }
    for(int i=0;i<pOther->mLWF.size();i++)
    {
        mLWF.push_back(pOther->mLWF[i]);
    }
    mLWNextIndex=pOther->mLWNextIndex;
}

int CListWidget::AddField(CPropertyField *pField,int pIndex,int pWidth)
{
    SField *lNew=new SField;

    if(pIndex<0) pIndex=mLWNextIndex++;

    lNew->mField=pField;
    lNew->mIndex=pIndex;
    lNew->mW=pWidth;

    SField *lLastVisible=nullptr;

    for(int i=mLWF.size()-1;i>=0;i--)
    {
        if(!(mLWF[i]->mField->mStyle&CPropertyField::PLF_HIDDEN))
        {
            lLastVisible=mLWF[i];
            break;
        }
    }

    if(lLastVisible) //not first
        lNew->mX=lLastVisible->mX+lLastVisible->mW;
    else
        lNew->mX=200;

    mLWF.push_back(lNew);

    for(int i=0;i<mLWI.size();i++)
        mLWI[i]->AddField(pField);

    OnLWFieldsChanged();

    return lNew->mIndex;
}

bool CListWidget::RemoveField(int pIndex)
{
    fatal_error("RemoveField not implemented\n");

    int lPos=GetFieldNum(pIndex);
    if(lPos<0) return false;
    delete mLWF[lPos];
    mLWF.erase(mLWF.begin()+lPos);

    OnLWFieldsChanged();

    return true;
}

void CListWidget::RemoveAllFields()
{
    fatal_error("RemoveallFields not implemented\n");

    for(int i=0;i<mLWF.size();i++)
    {
        delete mLWF[i];
    }

    mLWF.clear();

    OnLWFieldsChanged();
}

void CListWidget::AddGroup(const std::wstring &pGroup)
{
    mLWGroups.push_back(pGroup);

    OnLWBigChange();
}

void CListWidget::RemoveGroup(const std::wstring &pGroup)
{
    erase_first_equal(mLWGroups,pGroup);

    OnLWBigChange();
}

CListWidget::SItem *CListWidget::NewItem(const std::wstring &pName,PIcon pIcon,TID pID)
{
    SItem *lNew=new SItem(this);

    if(pID==INT_MIN) pID=mLWNextIndex++;

    lNew->mID=pID;
    lNew->mText=pName;
    lNew->mIcon=pIcon;

    return lNew;
}

void CListWidget::AddItem(SItem *pNew)
{
    mLWI.push_back(pNew);

    OnItemAdded(pNew);
    OnListChanged();
}

CListWidget::SItem *CListWidget::AddItem(const std::wstring &pName,PIcon pIcon,TID pID)
{
    SItem *lItem=NewItem(pName,pIcon,pID);
    AddItem(lItem);
    return lItem;
}

void CListWidget::AddItems(const std::vector<std::wstring> &pNames,PIcon pIcon,TID pID)
{
    for(int i=0;i<pNames.size();i++)
    {
        AddItem(pNames[i],pIcon,(pID==INT_MIN)?INT_MIN:(pID+i));
    }
}

void CListWidget::ReplaceItem(SItem *pWhich,SItem *pWith)
{
    *pWhich=*pWith;
    delete pWith;
}

bool CListWidget::RemoveItemByID(TID pID)
{
    int lPos=GetItemNum(pID);
    if(lPos<0) return false;
    SItem *lItem=mLWI[lPos];
    mLWI.erase(mLWI.begin()+lPos);
    OnItemRemoved(lItem);
    delete lItem;

    OnListChanged();

    return true;
}

void CListWidget::RemoveAllItems()
{
    std::vector<SItem*> lLBI=mLWI;

    mLWI.clear();

    for(int i=0;i<lLBI.size();i++)
    {
        OnItemRemoved(lLBI[i]);
        delete lLBI[i];
    }

    OnLWBigChange();
}

void CListWidget::SortItems()
{
    std::sort(mLWI.begin(),mLWI.end(),SSorter(false));

    OnLWBigChange();
}

TID CListWidget::GetSelID()
{
    for(int i=0;i<GetItemCount();i++)
    {
        if(mLWI[i]->mSelected) return mLWI[i]->mID;
    }

    return -1;
}

std::wstring CListWidget::GetSelText()
{
    for(int i=0;i<GetItemCount();i++)
    {
        if(mLWI[i]->mSelected) return mLWI[i]->mText;
    }

return std::wstring();
}

CListWidget::SItem* CListWidget::GetSelItem()
{
    for(int i=0;i<GetItemCount();i++)
    {
        if(mLWI[i]->mSelected) return mLWI[i];
    }

    return nullptr;
}

CListWidget::SItem *CListWidget::FindItem(const std::wstring &pText)
{
    int lCount=GetItemCount();
    for(int i=0;i<lCount;i++)
        if(mLWI[i]->mText==pText)
            return mLWI[i];
    return nullptr;
}

CListWidget::SField *CListWidget::GetField(int pIndex)
{
    int lPos=GetFieldNum(pIndex);
    if(lPos>=0) return mLWF[lPos];
    return nullptr;
}

CListWidget::SItem *CListWidget::GetItemByID(int pIndex)
{
    int lPos=GetItemNum(pIndex);
    if(lPos>=0) return mLWI[lPos];

    back_trace();
    throw XArgument("Wrong item index in CListWidget");
}

CListWidget::SItem *CListWidget::GetItemByPos(int pPos)
{
    if(pPos>=0) return mLWI[pPos];
    return nullptr;
}

int CListWidget::GetItemPos(SItem *pItem)
{
    int lCount=mLWI.size();
    for(int i=0;i<lCount;i++)
        if(mLWI[i]==pItem)
            return i;
    return -1;
}

void CListWidget::SelectAll()
{
    mLWSelected=0;
    for(int i=0;i<GetItemCount();i++)
    {
        if(mLWI[i]->mSelected) continue;

        mLWI[i]->mSelected=true;

        OnItemSelected(mLWI[i]);

        mLWSelected++;
    }
}

void CListWidget::UnselectAll()
{
    for(int i=0;i<GetItemCount();i++)
    {
        if(!mLWI[i]->mSelected) continue;

        mLWI[i]->mSelected=false;

        OnItemSelected(mLWI[i]); //Not selected
    }

    mLWSelected=0;
}

void CListWidget::SelectItem(SItem *pItem)
{
    if(!pItem)
        return;

    for(int i=0;i<GetItemCount();i++)
    {
        if(!mLWI[i]->mSelected) continue;
        if(mLWI[i]==pItem) continue;

        mLWI[i]->mSelected=false;
        OnItemSelected(mLWI[i]);
    }

    pItem->mSelected=true;

    OnItemSelected(pItem);

    mLWSelected=1;
}

void CListWidget::ToggleItem(SItem *pItem)
{
    if(!pItem) return;

    pItem->mSelected=(pItem->mSelected)?false:true;

    OnItemSelected(pItem);

    mLWSelected=(pItem->mSelected)?(mLWSelected+1):(mLWSelected-1);
}

void CListWidget::SelectItems(int pNum,SItem **pItems)
{
    int j;

    for(int i=0;i<GetItemCount();i++)
    {
        SItem *lItem=mLWI[i];
        for(j=0;j<pNum;j++)
        {
            if(lItem==pItems[j]) break;
        }
        if(j<pNum)
        {
            //found in list
            if(lItem->mSelected) continue;
            lItem->mSelected=true;

            OnItemSelected(mLWI[i]);
        }
        else
        {
            //not found
            if(!lItem->mSelected) continue;
            lItem->mSelected=false;

            OnItemSelected(mLWI[i]);
        }
    }
}

void CListWidget::SelectItemByID(TID pID)
{
    SelectItem(GetItemByID(pID));
}

void CListWidget::ToggleItemByID(TID pID)
{
    ToggleItem(GetItemByID(pID));
}

void CListWidget::SelectItemByName(const std::wstring &pName)
{
    SelectItem(FindItem(pName));
}

void CListWidget::ToggleItemByName(const std::wstring &pName)
{
    ToggleItem(FindItem(pName));
}

/*namespace gui*/ } /*namespace buola*/ }
